#include <Connectivity/TCPServer.hpp>

TCPServer::TCPServer(int port, bool ipv6) {

	/*
	 * Set port
	 */
	this->port = port;
	this->connected = 0;
	this->ipv6 = ipv6;

	/*
	 * set default values
	 */
	sockListen = -1;


	init();
}

int TCPServer::init() {

	int bind_status;

	/* Configure socket depending in ip version */
	if (ipv6) {

		struct sockaddr_in6 server;
		memset(&server, 0, sizeof(server));

		/* Create IPv6 socket */
		sockListen = socket(AF_INET6, SOCK_STREAM, 0);

		/* Setup configuration and bind it to socket */
		server.sin6_family = AF_INET6;
		server.sin6_addr = in6addr_any;
		server.sin6_port = htons(port);
		bind_status = bind(sockListen, (struct sockaddr*) &server,sizeof(server));

	} else {

		struct sockaddr_in server;
		memset(&server, 0, sizeof(server));

		/* Create IPv4 socket */
		sockListen = socket(AF_INET, SOCK_STREAM, 0);

		/* Setup configuration and bind it to socket */
		int opt = 1;
		if (setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, (char *) &opt,
				sizeof(opt)) == -1) {
			std::cerr << "Error: Setsockopt" << std::endl;
			return 1;
		}
		server.sin_family = AF_INET;
		server.sin_addr.s_addr = htonl( INADDR_ANY);
		server.sin_port = htons(port);
		bind_status = bind(sockListen, (struct sockaddr*) &server,sizeof(server));

	}

	/* Check if binding was successful */
	if (bind_status < 0) {
		std::cerr << "Error: Binding" << std::endl;
		return 2;
	}

	/* Start to listen */
	int listen_status = listen(sockListen, 5);
	if (listen_status == -1) {
		std::cerr << "Error: Listen" << std::endl;
		return 3;
	}

	int flags = fcntl(sockListen, F_GETFL, 0);
	if(flags == -1){
		std::cerr << "fcntl()" << std::endl;
	}

	flags |= O_NONBLOCK;
	flags = fcntl(sockListen, F_SETFL, flags);
	if(flags == -1){
		std::cerr << "fcntl()" << std::endl;
	}

	return 0;

}

TCPConnection *TCPServer::waitForConnection() {

	int clientSocket;
	struct sockaddr *client;
	socklen_t len;

	/* Set len of configuration based on ip version */
	if(ipv6){
		len = sizeof(struct sockaddr_in6);
	}else{
		len = sizeof(struct sockaddr_in);
	}

	/* Get incoming connection */
	client = (struct sockaddr*) malloc(len);
	clientSocket = accept(sockListen, (struct sockaddr*) client, &len);


	/* Create a new TCP connection and return it */
	if (clientSocket >= 0) {
		return new TCPConnection(ipv6, clientSocket, client);

	} else {
		/*
		 * Something went wrong
		 */
		std::cerr << "Error: Accept" << std::endl;
		return 0;
	}

}

void TCPServer::closeServer() {

	/*
	 * Close listening socket
	 */
	close(sockListen);

}

int TCPServer::getSock(){
	return sockListen;
}
